﻿using RedLockNet.SERedis;
using RedLockNet.SERedis.Configuration;
using StackExchange.Redis;
using System;
using System.Linq;
using System.Net;
using Uiap.Infrastructure.Configurations;

namespace Uiap.Infrastructure.Runtime.Caching.Redis
{
    public class RedisConnectionWrapper : IRedisConnectionWrapper, ILocker
    {

        #region Fields

        private readonly RedisConnectConfig _config;

        private readonly object _lock = new object();
        private volatile ConnectionMultiplexer _connection;
        private readonly Lazy<string> _connectionString;
        private volatile RedLockFactory _redisLockFactory;

        #endregion

        #region Ctor

        public RedisConnectionWrapper(RedisConnectConfig config)
        {
            _config = config;
            _connectionString = new Lazy<string>(GetConnectionString);
            _redisLockFactory = CreateRedisLockFactory();
        }

        #endregion

        #region Utilities

        /// <summary>
        /// 获取redis服务器连接字符串
        /// </summary>
        /// <returns></returns>
        protected string GetConnectionString()
        {
            return _config.ConnectionString;
        }

        /// <summary>
        /// 获取redis服务器连接
        /// </summary>
        /// <returns></returns>
        protected ConnectionMultiplexer GetConnection()
        {
            if (_connection != null && _connection.IsConnected) return _connection;

            lock (_lock)
            {
                if (_connection != null && _connection.IsConnected) return _connection;

                //如果连接已经关闭，则释放
                _connection?.Dispose();

                //创建新连接
                _connection = ConnectionMultiplexer.Connect(_connectionString.Value);
            }

            return _connection;
        }

        /// <summary>
        /// 创建RedLock factory实例
        /// </summary>
        /// <returns>RedLock factory</returns>
        protected RedLockFactory CreateRedisLockFactory()
        {
            //获取RedLock端点
            var configurationOptions = ConfigurationOptions.Parse(_connectionString.Value);
            var redLockEndPoints = GetEndPoints().Select(endPoint => new RedLockEndPoint
            {
                EndPoint = endPoint,
                Password = configurationOptions.Password,
                Ssl = configurationOptions.Ssl,
                RedisDatabase = configurationOptions.DefaultDatabase,
                ConfigCheckSeconds = configurationOptions.ConfigCheckSeconds,
                ConnectionTimeout = configurationOptions.ConnectTimeout,
                SyncTimeout = configurationOptions.SyncTimeout
            }).ToList();

            //使用RedLock分布式锁算法创建实例
            return RedLockFactory.Create(redLockEndPoints);
        }

        #endregion

        #region Methods

        /// <summary>
        /// 获取redis数据库交互连接
        /// </summary>
        /// <param name="db">数据库编号</param>
        /// <returns></returns>
        public IDatabase GetDatabase(int db)
        {
            return GetConnection().GetDatabase(db);
        }

        /// <summary>
        /// 获取redis独立服务器配置API
        /// </summary>
        /// <param name="endPoint">网络端点</param>
        /// <returns>Redis server</returns>
        public IServer GetServer(EndPoint endPoint)
        {
            return GetConnection().GetServer(endPoint);
        }

        /// <summary>
        /// 获取服务器所有端点
        /// </summary>
        /// <returns></returns>
        public EndPoint[] GetEndPoints()
        {
            return GetConnection().GetEndPoints();
        }

        /// <summary>
        /// 删除数据库所有key
        /// </summary>
        /// <param name="db">数据库编号</param>
        public void FlushDatabase(RedisDatabaseNumber db)
        {
            var endPoints = GetEndPoints();

            foreach (var endPoint in endPoints)
            {
                GetServer(endPoint).FlushDatabase((int)db);
            }
        }

        /// <summary>
        /// 使用分布式独占锁（排它锁）执行操作
        /// </summary>
        /// <param name="resource">加锁的Key</param>
        /// <param name="expirationTime">Key过期时间，过期自动释放锁</param>
        /// <param name="action">操作逻辑</param>
        /// <returns>True：成功获取锁并且成功执行方法; False：未能获取锁或者未执行</returns>
        public bool PerformActionWithLock(string resource, TimeSpan expirationTime, Action action)
        {
            using (var redisLock = _redisLockFactory.CreateLock(resource, expirationTime))
            {
                //确保已经获得锁
                if (!redisLock.IsAcquired)
                    return false;

                //执行操作
                action();

                return true;
            }
        }

        /// <summary>
        /// 释放当前实例的所有资源
        /// </summary>
        public void Dispose()
        {
            //释放redis连接
            _connection?.Dispose();

            //释放RedLock factory
            _redisLockFactory?.Dispose();
        }

        #endregion
    }
}
